package pl;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.io.RandomAccessFile;



public class analizadorLexico {

    public reservadas tablaReservadas;
    public Boolean juntant;
    public nodoAFD arrel;
    public nodoAFD posicio;
    public RandomAccessFile arxiu;
    public ArrayList<lletraFilaColumna> buffer;
    public int fila;
    public int columna;
    public ArrayList<lletraFilaColumna> tokenTemporal;

    public class lletraFilaColumna {

        char lletra;
        int fila;
        int columna;

        public lletraFilaColumna(char a, int fil, int col) {
            lletra = a;
            fila = fil;
            columna = col;
        }
    }

    public analizadorLexico() {


        ArrayList<nodoAFD> aux;
        ArrayList<nodoAFD> aux2;
        ArrayList<Character> intervalos;
        nodoAFD naux;
        nodoAFD naux2;
        nodoAFD naux3;
        fila = 1;
        columna = 0;
        arxiu = null;
        buffer = new ArrayList<lletraFilaColumna>();
        tokenTemporal = new ArrayList<lletraFilaColumna>();
        juntant = false;
        tablaReservadas = new reservadas();
        arrel = new nodoAFD("arrel", 0, null, false, new ArrayList<nodoAFD>());
        posicio = arrel;

        intervalos = new ArrayList<Character>();
        intervalos.add('(');
        arrel.hijos.add(new nodoAFD("pari", 1, intervalos, true, new ArrayList<nodoAFD>()));

        intervalos = new ArrayList<Character>();
        intervalos.add(')');
        arrel.hijos.add(new nodoAFD("pard", 2, intervalos, true, new ArrayList<nodoAFD>()));

        intervalos = new ArrayList<Character>();
        intervalos.add(';');
        arrel.hijos.add(new nodoAFD("pyc", 3, intervalos, true, new ArrayList<nodoAFD>()));

        intervalos = new ArrayList<Character>();
        intervalos.add('=');
        arrel.hijos.add(new nodoAFD("asig", 4, intervalos, true, new ArrayList<nodoAFD>()));

        intervalos = new ArrayList<Character>();
        intervalos.add('{');
        arrel.hijos.add(new nodoAFD("llavei", 5, intervalos, true, new ArrayList<nodoAFD>()));

        intervalos = new ArrayList<Character>();
        intervalos.add('}');
        arrel.hijos.add(new nodoAFD("llaved", 6, intervalos, true, new ArrayList<nodoAFD>()));

        intervalos = new ArrayList<Character>();
        intervalos.add('0');
        intervalos.add('9');
        aux = new ArrayList<nodoAFD>();
        naux = new nodoAFD("entero", 100, intervalos, true, aux);
        aux.add(naux);

        aux2 = new ArrayList<nodoAFD>();
        naux2 = new nodoAFD("real", 101, intervalos, true, aux2);
        aux2.add(naux2);

        intervalos = new ArrayList<Character>();
        intervalos.add('.');
        naux3 = new nodoAFD("punto", 102, intervalos, false, aux2);
        aux.add(naux3);
        arrel.hijos.add(naux);

        intervalos = new ArrayList<Character>();
        intervalos.add('a');
        intervalos.add('z');
        intervalos.add('A');
        intervalos.add('Z');
        aux = new ArrayList<nodoAFD>();
        naux = new nodoAFD("id", 103, intervalos, true, aux);
        aux.add(naux);

        intervalos = new ArrayList<Character>();
        intervalos.add('a');
        intervalos.add('z');
        intervalos.add('A');
        intervalos.add('Z');
        intervalos.add('0');
        intervalos.add('9');
        aux2 = new ArrayList<nodoAFD>();
        naux2 = new nodoAFD("id", 103, intervalos, true, aux2);
        aux2.add(naux2);
        aux.add(naux2);
        arrel.hijos.add(naux);

        intervalos = new ArrayList<Character>();
        intervalos.add('/');
        naux = new nodoAFD("comentario",204,intervalos,true,new ArrayList<nodoAFD>());

        intervalos = new ArrayList<Character>();
        intervalos.add('*');
        aux = new ArrayList<nodoAFD>();
        naux2 = new nodoAFD("accierra",203,intervalos,false,aux);
        aux.add(naux);
        aux.add(naux2);


        intervalos = new ArrayList<Character>();
        intervalos.add((char)0);
        intervalos.add((char)(((int)'*')-1));
        intervalos.add((char)(((int)'*')+1));
        intervalos.add((char)255);
        aux2 = new ArrayList<nodoAFD>();

        naux= new nodoAFD("xcsumidero",202,intervalos,false,aux2);
        aux2.add(naux2);
        aux2.add(naux);
        aux.add(naux);

        intervalos = new ArrayList<Character>();
        intervalos.add('*');
        aux = new ArrayList<nodoAFD>();
        aux.add(naux);
        aux.add(naux2);
        naux3 = new nodoAFD("acabre",201,intervalos,false,aux);


        intervalos = new ArrayList<Character>();
        intervalos.add('/');
        aux2= new ArrayList<nodoAFD>();
        aux2.add(naux3);
        naux= new nodoAFD("bcabre",200,intervalos,false,aux2);

        arrel.hijos.add(naux);
    }

     public nodoToken muntaNodoToken(nodoAFD posicio, lletraFilaColumna clot) throws nodoToken {
        nodoToken tornada = new nodoToken();

        if (clot != null) {
            if (posicio.acepta) {
                tornada.cadena = Character.toString(clot.lletra);
                tornada.columna = clot.columna;
                tornada.fila = clot.fila;
                tornada.token = posicio.token;
            } else {
                tornada = muntaNodoError1(clot);
            }
        } else {
            if (posicio.acepta) {
                if (tokenTemporal.size() > 0 && tokenTemporal.get(0) != null) {
                    tornada.columna = tokenTemporal.get(0).columna;
                    tornada.fila = tokenTemporal.get(0).fila;
                    tornada.cadena = "";
                    for (int i = 0; i < tokenTemporal.size(); i++) {
                        if (tokenTemporal.get(i) != null) {
                            tornada.cadena += tokenTemporal.get(i).lletra;
                        }
                    }

                    tokenTemporal.clear();
                    tornada.token = posicio.token;
                    if (tornada.token.equals("id")) {
                        tornada.token = idsReservades(tornada.cadena);
                    }
                } else {
                    tornada = muntaNodoEOF();
                }
            } else {
                tornada = muntaNodoError1(tokenTemporal.get(tokenTemporal.size() - 1));
            }
        }
        return (tornada);
    }
    public nodoToken obtinToken(lletraFilaColumna clot) throws nodoToken  {

        Character c;
        lletraFilaColumna clotet;
        nodoToken tornada = null;
        Boolean ensMovem = false;
        nodoAFD aux = null;

        buffer.add(clot);
        clotet = buffer.remove(0);
        if (clotet != null) {
            c = clotet.lletra;
            aux = posicio;
            for (int i = 0; !ensMovem && i < posicio.hijos.size(); i++) {
                if (posicio.hijos.get(i).pertenece(c)) {
                    posicio = posicio.hijos.get(i);
                    ensMovem = true;
                    if (posicio.tipus >= 100) {
                        juntant = true;
                    }
                }
            }
            if (ensMovem) {
                if (juntant) {
                    tornada = null;
                    tokenTemporal.add(clotet);

                } else {
                    tornada = muntaNodoToken(posicio, clotet);
                    posicio = arrel;
                }
            } else {
                if (juntant) {
                    juntant = false;
                    tornada = muntaNodoToken(aux, null);
                    buffer.add(0, clotet);
                    posicio = arrel;
                } else {
                    if (arribemABonPortDesDeLarrel(clotet.lletra)) {
                        buffer.add(0, clotet);
                        tornada = null;
                    } else {
                        if (!esSeparador(clotet.lletra)) {

                            tornada = muntaNodoError1(clotet);
                        } else {
                            tornada = null;
                        }
                    }
                    posicio = arrel;
                }
            }
        } else {
            tornada = muntaNodoEOF();
        }
        return tornada;
    }

    public nodoToken muntaNodoError1(lletraFilaColumna clot) throws nodoToken  {
        nodoToken tornada = new nodoToken();

        tornada.cadena = Character.toString(clot.lletra);
        tornada.columna = clot.columna;
        tornada.fila = clot.fila;
        tornada.token = "ERROR1";
        throw tornada;
    }

     public nodoToken muntaNodoError2(lletraFilaColumna clot) throws nodoToken  {
        nodoToken tornada = new nodoToken();

        tornada.cadena = "";
        tornada.columna = columna;
        tornada.fila = fila;
        tornada.token = "ERROR2";
        throw tornada;
    }

    public boolean esSeparador(char c) {
        boolean tornada = false;

        if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
            tornada = true;
        }
        return (tornada);
    }

    public nodoToken muntaNodoEOF() {
        nodoToken tornada = new nodoToken();

        tornada.cadena = "";
        tornada.columna = columna;
        tornada.fila = fila;
        tornada.token = "EOF";

        return (tornada);
    }

    public boolean arribemABonPortDesDeLarrel(char c) {
        boolean ensMovem = false;
        nodoAFD reposicio = arrel;

        for (int i = 0; i < reposicio.hijos.size() && !ensMovem; i++) {
            if (reposicio.hijos.get(i).pertenece(c)) {
                ensMovem = true;
            }
        }
        return (ensMovem);
    }

    public boolean carrega(String ruta) throws FileNotFoundException {
            arxiu = new RandomAccessFile(ruta,"r");
            return (arxiu!=null);
        }




    public String idsReservades(String cadena) {

        String id = tablaReservadas.obten(cadena);
        if (id == null) {
            id = "id";
        }
        return (id);
    }

    public int llig() {
        int i = -1;
        try {
            i = arxiu.read();
            columna++;
            if (i == (int) '\n') {
                fila++;
                columna = 0;
            }
        } catch (IOException ex) {
        //    Logger.getLogger(analizadorLexico.class.getName()).log(Level.SEVERE, null, ex);
        }

        return (i);
    }

  public nodoToken esprem() throws nodoToken {

        nodoToken tornada = null;
        lletraFilaColumna clot = null;
        int aux = 0;


        while (tornada == null) {
            aux = llig();
            if (aux > 0) {
                clot = new lletraFilaColumna((char) aux, fila, columna);
            } else {
                clot = null;
            }
            tornada = obtinToken(clot);
            if (tornada != null && tornada.token.equals("comentario")) {
                tornada = null;
            }
        }
        if (tornada.token.equals("EOF")) {
            if (posicio.tipus > 200) {
                if(!posicio.acepta){
                    tornada = muntaNodoError2(clot);
                }
            } else if (posicio.tipus >= 100) {
                if (!(posicio.tipus==100 || posicio.tipus==103)) {
                    if(tokenTemporal.size()>0){
                        tornada = muntaNodoError1(new lletraFilaColumna(tokenTemporal.get(tokenTemporal.size()-1).lletra, tokenTemporal.get(tokenTemporal.size()-1).fila, tokenTemporal.get(tokenTemporal.size()-1).columna));
                    } else {
                        tornada = muntaNodoError1(new lletraFilaColumna(tokenTemporal.get(tokenTemporal.size()-1).lletra, fila, columna));
                    }
                } else {
                    tornada = muntaNodoToken(posicio, clot);
                }
            }

        }
        return (tornada);
    }
}
